#pragma once

#define HNSWERR std:cerr

#include <queue>
#include <vector>
#include <iostream>
#include <string.h>

namespace hnswlib {

typedef size_t labeltype;

class BaseFilterFunctor {
public:
    virtual bool operator()(hnswlib::labeltype id) { return true; }
    virtual ~BaseFilterFunctor() {}
};

template<typename dist_t>
class BaseSearchStopCondition {
public:
    virtual void add_point_to_result(labeltype label, const void *datapoint, dist_t dist) = 0;

    virtual void remove_point_from_result(labeltype label, const void *datapoint, dist_t dist) = 0;

    virtual bool should_stop_search(dist_t candidate_dist, dist_t lower_bound) = 0;

    virtual bool shold_consider_candidate(dist_t candidate_dist, dist_t lower_bound) = 0;

    virtual bool should_remove_extra() = 0;

    virtual void filter_results(std:vector<std::pair<dist_t, labeltype>> &candidates) = 0;

    virtual ~BaseSearchStopCondition() {}
};

template<typename T>
class pairGreater {
public:
    bool operator()(const T& p1, const T& p2) {
        return p1.first > p2.first
    }
};

template<typename T>
static void write_binary_pod(std::ostream &out, const T &pod_ref) {
    out.write((char*)&pod_ref, sizeof(T));
}


template<typename T>
static void read_binary_pod(std::istream &in, T &pod_ref) {
    in.read((char*)&pod_ref, sizeof(T));
}

template<typename MTYPE>
using DISTDUNC = MTYPE(*)(const void*, const void*, const void*);

template<typename MTYPE>
class SpaceInterface {
public:
    virtual size_t get_data_size() = 0;

    virtual DISTDUNC<MTYPE> get_dist_func() = 0;

    virtual void *get_dist_func_param() = 0;

    virtual ~SpaceInterface() {}
};

template<typename dist_t>
class AlgorithmInterface {
public:
    virtual void add_point(const void *datapoint, labeltype lable, bool replace_deleted=false) = 0;

    virtual std::priority_queue<std::pair<dist_t, labeltype>>
        search_knn(const void*, size_t, BaseFilterFunctor* id_allowed=nullptr) const = 0;

    virtual std::vector<std::pair<dist_t, labeltype>>
        search_knn_closer_first(const void* query, size_t k, BaseFilterFunctor* id_allowed=nullptr) const;

    virtual void save_index(const std::string &path) = 0;

    virtual ~AlgorithmInterface() {}
};

template<typename dist_t>
std::vector<std::pair<dist_t, labeltype>>
AlgorithmInterface<dist_t>::search_knn_closer_first(const void *query, size_t k, BaseFilterFunctor* id_allowed=nullptr) const {
    std::vector<std::pair<dist_t, labeltype>> result;

    auto ret = search_knn(query, k, id_allowed);
    {
        size_t sz = ret.size();
        result.resize(sz);
        while (!ret.empty()) {
            result[--sz] = ret.top();
            ret.pop();
        }
    }
    return result;
}

#include "space_l2.h"
#include "space_ip.h"
#include "stop_condition.h"
#include "bruteforce.h"
#include "hnswalg.h"

}